package com.jse.web;

import java.io.File;
import java.io.IOException;
import java.net.HttpCookie;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;

import javax.script.CompiledScript;

import com.jse.Fs;
import com.jse.Ioc;
import com.jse.Js;
import com.jse.Jse;
import com.jse.Lang;
import com.jse.Three;
import com.jse.Tuple;
import com.jse.jdbc.Jdbc;
import com.jse.json.JsonObject;
import com.sun.net.httpserver.HttpExchange;

public class Web {
	
	public static WeakHashMap<String,HttpSession> SESSIONS=new WeakHashMap<>();
	public final static Set<String> STATIC_PRIFIX=Lang.ofSet("/favicon.ico","/upload/","/assets/","/image/","/img/","/js/","/css/",
			"/font/","/fonts/","/public/","/static/","/404.html","/403.html","/500.html");
	public final static Set<String> STATIC_SUFFIX=Lang.ofSet("ico","css","js","htm","txt","mjs","xml",
			"jpg","png","jpeg","gif","webp","bmp","svg","swf","map","crdownload",
			"aac","avi","mp4","mp3","mdi","csv","doc","docx","xls","xlsx","ppt","pptx","rtf","wav","pdf",
			"otf","tif","ttf","eot","tiff","woff","woff2","7z","rar","tar","zip"
			);//后缀可定制取消
	public final static Map<String,Three<Path,Long,Tuple<CompiledScript,List<String>>>> FILTER_FUNS=new HashMap<>();//过滤器
	
	public final static ScopedValue<Tbl> RQRS =  ScopedValue.newInstance();
	public static int existsNot=-1;
	
	public static boolean isStatic(String url,String suffix) {
		if(Lang.startsWith(url, STATIC_PRIFIX)) {
			return true;
		}else if(STATIC_SUFFIX.contains(suffix)) {
			return true;
		}
		return false;
	}
	
	public static boolean filter(HttpExchange ex, String url,boolean before) {
		var filtermapping=Lang.startsWithGet(url,Web.FILTER_FUNS.keySet());
		if(filtermapping!=null) {//拦截器
			var t=Web.FILTER_FUNS.get(filtermapping);
			if(before) {//是前置方法
				var name=t.c().b().contains("before")?"before":"main";
				var r=Js.cFun(t.a(),name,ex);
				if(r!=null&&r!=Boolean.TRUE)
				try {View.filter.render(ex,r);return false;} catch (Exception e){throw new RuntimeException(e);}
			}else {
				if(t.c().b().contains("after")) {//是后置方法并且存在 注册时没有则不会执行除非手动添加
					var r=Js.cFun(t.a(),"after",ex);
					if(r!=null&&r!=Boolean.TRUE)
					try {View.filter.render(ex,r);return false;} catch (Exception e){throw new RuntimeException(e);}
				}
			}
		}
		return true;
	}
	public static void init() throws Exception {
		if(Jse.tempDir==null)Jse.tempDir=new File(System.getProperty("java.io.tmpdir"));
		if(Jse.conf.containsKey("jse.session.timeout"))setSessionTimeout(Jse.conf.getInt("jse.session.timeout"));//设置超时
		if(Jse.webapp()==null) {
			if(Files.exists(Path.of(Jse.dir,"web"))) {
				Jse.setWebapp(Jse.dir+"/web");
				Jse.setWebapps(Jse.dir);
			}
		}
		if(Jse.webapp()==null) {//可能存在未设置webapp等，自动获取下
			Jse.setWebapp(Jse.dir);
			if(Jse.webapp().endsWith("/"))Jse.setWebapp(Jse.webapp().substring(0,Jse.webapp().length()-1));
			Jse.setWebapps(Jse.webapp().substring(0,Jse.webapp().lastIndexOf('/')));
			if(Jse.webapp().contains("tomcat"))Jse.setWebapps(Jse.webapps()+"/webapps");//使用tomcat时的默认webapps
		}
		Web.data("root","/");//设置默认root变量 如页面中 #(root)
		Jdbc.start();
		Js.execute("var Web=Java.type('com.jse.web.Web');");
		Ioc.BEANS.forEach((name,bean)->{
			if(name.indexOf('.')==-1)Js.set(name, bean);
		});
		var JPATH=Path.of(Jse.jspath());
		Files.walkFileTree(JPATH,EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
				new SimpleFileVisitor<Path>() {
					@Override
					public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) throws IOException {
						String f=p.toString().substring(Jse.jspath().length()).replace('\\','/');
						String suffix=Fs.suffix(f);//后缀
						String mapping=f.substring(0,f.length()-suffix.length()-1);
						try {
							if(mapping.startsWith("/$/")&&"js".equals(suffix)) {//过滤器
								String mapping1=mapping.equals("/$/$")?"":mapping.substring(2);
								FILTER_FUNS.put(mapping1+"/",new Three<Path,Long,Tuple<CompiledScript,List<String>>>
								(p,Fs.lastModifiedTime(p),Js.compiles(p)));
							}
//							else if(mapping.endsWith("id")&&"js".equals(suffix)){//包含正则映射
//								var jsc=Js.js.compile(Files.readString(p));
//								REGEX_FUNS.put(mapping.substring(0,mapping.length()-2)+"{id:\\d+}",
//									new Three<Path,Long, CompiledScript>(p,Files.getLastModifiedTime(p).toMillis(), jsc));
//							}else if(mapping.endsWith("name")&&"js".equals(suffix)){//包含正则映射
//								var jsc=Js.js.compile(Files.readString(p));
//								REGEX_FUNS.put(mapping.substring(0,mapping.length()-2)+"{name}",
//									new Three<Path,Long, CompiledScript>(p,Files.getLastModifiedTime(p).toMillis(), jsc));
//							}else if("js".equals(suffix)){//固定映射
//								var jsc=Js.js.compile(Files.readString(p));
//								FIXED_FUNS.put(mapping,
//									new Three<Path,Long, CompiledScript>(p,Files.getLastModifiedTime(p).toMillis(), jsc));
//							}
						} catch (Exception e2) {
							e2.printStackTrace();
							throw new RuntimeException("编译出错:"+f);
						}
						return super.visitFile(p, attrs);
					}
		});
		if(Fs.exists(Jse.jspath()+"/jse.js"))
		Js.execute(Fs.read(Jse.jspath()+"/jse.js"));
		System.out.println("classpath:"+Jse.classpath);
		System.out.println("jspath:"+Jse.jspath());
		System.out.println("webapps:"+Jse.webapps());
		System.out.println("webapp:"+Jse.webapp());
	}
	
	public final static Map<String,Object> data=new HashMap<>();//servletContent attr
	public static void data(String name,Object val) {data.put(name,val);}
	public static Object data(String name) {return data.get(name);}
	public static void attr(String name,Object val) {RQRS.get().setAttribute(name,val);}
	public static Object attr(String name) {return RQRS.get().getAttribute(name);}
	public static void sattr(String name,Object val) {RQRS.get().session().setAttribute(name, val);}
	public static Object sattr(String name) {return RQRS.get().session().getAttribute(name);}
	static int sessionTimeout=30*60;
	
	private static void setSessionTimeout(Integer timeout) {
		sessionTimeout=timeout;
	}

	private static class App {
        private static Web web = new Web();
    }
	public static Web web() {return App.web;}
	
	public static Map<String,HttpCookie> cookies(HttpExchange ex) {
        String cookiestr = ex.getRequestHeaders().getFirst("Cookie");
        if (cookiestr==null||cookiestr.isEmpty()) {cookiestr = ex.getResponseHeaders().getFirst("Set-Cookie");}
        Map<String,HttpCookie> cookies=new HashMap<>();
        if (cookiestr==null||cookiestr.isEmpty()) {return cookies;}
        String[] cookiearry = cookiestr.split(";");
        for(String ck : cookiearry){
        	String[] cks=ck.split("=");
        	cookies.put(cks[0],new HttpCookie(cks[0], cks[1]));
        }
        return cookies;
    }
	public static HttpSession session(HttpExchange ex) {
		String sid=getSessionId(ex);
		if(sid==null||sid.isEmpty()){sid=Lang.snowflake()+"";}
		if(SESSIONS.containsKey(sid))return SESSIONS.get(sid);
		HttpSession httpSession = new HttpSession(sid);
        SESSIONS.put(sid,httpSession);
		return httpSession;
	}
	
	public static String getSessionId(HttpExchange ex) {
		var c=cookies(ex).get("JSESSIONID");
		return c!=null?c.getValue():null;
	}

	public static Map<String, Object> datas(Map<String, Object> map) {
		data.forEach((k,v)->map.putIfAbsent(k, v));
		var s=RQRS.get().session();
		map.put("session",RQRS.get().session().attributes());
		return map;
	}

	public static boolean existsNot() {
		if(existsNot==-1) {
			if(Fs.exists(Jse.webapp()+"/404.html")) {
				existsNot=1;
			}else {
				existsNot=0;
			}
		}
		return existsNot==1;
	}

	public static boolean isWebSocket(HttpExchange ex) {//"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
		return (ex.getRequestHeaders().get("Upgrade").contains("websocket")&&
                "GET".equals(ex.getRequestMethod()));
    }

	
	
}
